import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;
import java.util.Vector;

import ownclasses.CircumscribedCircle;

import ij.IJ;
import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;


public class A01_Find_Circle implements PlugInFilter {

	static int CcAmount = 400;					// width of the new image
	ArrayList<Point> allPoints;
	ArrayList<CircumscribedCircle> cc;
	Point pa;
	Point pb;
	Point pc;
	static double tolerance = 0.05;
	
	public int setup(String arg0, ImagePlus im) {
		return DOES_8G;
	}
	
	
	public void run(ImageProcessor ip) {
		
		if (!getUserInput()) {
			return;
		}
		Random rg = new Random();
		allPoints = new ArrayList<Point>();
		cc = new ArrayList<CircumscribedCircle>();
		ImageProcessor ip2 = ip.duplicate();
		
		//Find all Points in Image
		for(int i = 0; i <= ip2.getHeight()+1; i++)
		{
			for(int j = 0; j <= ip2.getWidth(); j++)
			{
				if(ip2.getPixel(j, i) > 0)
				{
					allPoints.add(new Point(j, i));
				}
			}
		}
		
//		IJ.log("Gefundene Punkte: " + allPoints.size());
		
	
		for(int i = 0; i < CcAmount; i++)
		{
			//Picking 3 random Points
			int index1 = rg.nextInt(allPoints.size());
			int index2;
			int index3;
			do{
				index2 = rg.nextInt(allPoints.size());
			}while(index2 == index1);
			do{
				index3 = rg.nextInt(allPoints.size());
			}while(index3 == index1 || index3 == index2);
			
			pa = allPoints.get(index1);
			pb = allPoints.get(index2);
			pc = allPoints.get(index3);
			
			double a = Math.sqrt(Math.pow(pa.x - pb.x, 2) + Math.pow(pa.y - pb.y, 2));
			double b = Math.sqrt(Math.pow(pb.x - pc.x, 2) + Math.pow(pb.y - pc.y, 2));
			double c = Math.sqrt(Math.pow(pc.x - pa.x, 2) + Math.pow(pc.y - pa.y, 2));
			
			//Calculating Circumscribed Circle
			//IJ.log("a: "+ a);
			//IJ.log("b: "+ b);
			//IJ.log("c: "+ c);
			
			double D = 2 * (pa.x*(pb.y-pc.y)+pb.x*(pc.y-pa.y)+pc.x*(pa.y-pb.y));
			double diameter = (2*a*b*c)/(Math.sqrt((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c)));
			//IJ.log("Diameter: " + diameter);
			double Ux = ((Math.pow(pa.x, 2) + Math.pow(pa.y,2))*(pb.y - pc.y) + (Math.pow(pb.x, 2) + Math.pow(pb.y, 2))*(pc.y - pa.y) + (Math.pow(pc.x, 2) + Math.pow(pc.y, 2))*(pa.y - pb.y)) / D;
			double Uy = ((Math.pow(pa.x, 2) + Math.pow(pa.y,2))*(pc.x - pb.x) + (Math.pow(pb.x, 2) + Math.pow(pb.y, 2))*(pa.x - pc.x) + (Math.pow(pc.x, 2) + Math.pow(pc.y, 2))*(pb.x - pa.x)) / D;
			
			int closePoints = 0;
			for(int j = 0; j < allPoints.size(); j++)
			{
				if(allPoints.get(j) == pa || allPoints.get(j) == pb || allPoints.get(j) == pc)
				{
					
				}
				else
				{
					Point act = allPoints.get(j);
					double dist = Math.sqrt(Math.pow(Ux-act.x, 2)+Math.pow(Uy-act.y, 2));
					if(dist < ((diameter/2.0) + tolerance) && dist > ((diameter /2.0) - tolerance))
					{
						closePoints++;
					}
				}
			}
			
			cc.add(new CircumscribedCircle(new Point((int)Ux, (int)Uy), (int)(diameter/2.0), closePoints));
		}

//			IJ.log("Calculated Circles: " + cc.size());
		
		//Find the one with the most clsoePoints and draw it
		CircumscribedCircle bigest = cc.get(0);
		for(int i = 1; i < cc.size(); i++)
		{
			if(bigest.getM_NumberOfPointsInRange() < cc.get(i).getM_NumberOfPointsInRange())
			{
				bigest = cc.get(i);
			}
		}

		// Draw circle
		IJ.log("Center X: " + bigest.getM_Center().x);
		IJ.log("Center Y: " + bigest.getM_Center().y);
		IJ.log("Radius: " + bigest.getM_Radius());
		ip2.setColor(255);
		ip2.drawOval((int)(bigest.getM_Center().x-bigest.getM_Radius()), (int)(bigest.getM_Center().y-bigest.getM_Radius()), (int)(bigest.getM_Radius()*2.0), (int)(bigest.getM_Radius()*2.0));
		
		double distance = Math.sqrt(Math.pow(bigest.getM_Center().x - 200.0, 2)+Math.pow(bigest.getM_Center().y - 150.0, 2));
		
//		IJ.log("Genauigkeit: "+distance);
		
		// display the new image
		(new ImagePlus("Find algebraic line", ip2)).show();
		
	}

	boolean getUserInput() {
		GenericDialog gd = new GenericDialog("Amount of Circumscribed Circles");
		gd.addNumericField("Calculations", CcAmount, 0);
		gd.addNumericField("Tolerance", tolerance, 3);
		gd.showDialog();
		if (gd.wasCanceled()) {
			return false;
		}
		CcAmount = (int) gd.getNextNumber();
		tolerance = gd.getNextNumber();
		return true;
	}

}
